iT邦幫忙

2026 iThome 鐵人賽

DAY 27
0
Vibe Coding

Vibe Mode 開啟:30 天用 AI 打造網頁,邊做邊學 JavaScript系列 第 27 篇

Day 27:監控與 Log 系統:整合 Sentry / Logtail,建立發布後的錯誤監控

  • 分享至 

  • xImage
  •  

今日目標

昨日我們成功將 AI 個人財務追蹤器部署上雲(Vercel + Neon PostgreSQL),擁有了正式公開的網址!然而,應用程式順利上線並不代表開發過程的終結,而是運維(Observability)的開始。

當真實使用者開始進入系統,各種在本地開發或 CI 測試中沒被觸發的「幽靈 Bug」就會陸續浮現:例如使用者輸入了特殊字元導致 LLM API 噴出 500 錯誤、或是特定瀏覽器核心產生的 Hydration Mismatch。

今天我們的目標是建立產品發布後的防禦天眼:

  1. 整合 Sentry:自動捕捉前端 Client-side 與 Server-side Unhandled Exceptions,並附加完整的 Error Stack Trace。
  2. 整合 Logtail (Better Stack):建置結構化日誌(Structured Logging),追蹤 API 呼叫鏈與 LLM 請求耗時。
  3. 客製化 AI 錯誤捕捉與用戶上下文(User Context):精準定位是哪位使用者在呼叫哪項財務分析功能時發生異常!

Human vs. AI 實戰記錄

在整合第三方 SDK 時,AI 經常給出「舊版套件寫法」或「直接在元件裡用 console.error」這種粗暴的做法。今天我們需要訓練 AI 給出符合 Next.js App Router 最新架構的非同步錯誤攔截機制與結構化 Logger 封裝。


1. 給 AI 的指令 (Prompt)

我們希望 AI 幫我們在 Next.js App Router 專案中配置 Sentry 錯誤追蹤與 Logtail 結構化 Log Logger。

你是一位資深 SRE (Site Reliability Engineer) 與全棧架構師。
請幫我在 Next.js (App Router) 專案中整合 Sentry 與 Logtail (Better Stack) 監控系統。

需求如下:
1. 建立一個可重用的結構化 Logger 工具(`lib/logger.ts`),封裝 Winston 或 Pino / Logtail,支援 debug, info, warn, error 層級,並能紀錄運作時間 (duration) 與 userId。
2. 撰寫 Next.js `global-error.tsx` 與 `error.tsx` 組件,將前端 React 渲染崩潰自動回報至 Sentry。
3. 封裝一個 Server-side API 錯誤包裝函式(`withMonitoring`),自動記錄 API 請求狀態碼、耗時、捕獲 Exception 送往 Sentry,同時將 log 結構化推送到 Logtail。


2. AI 產出的結果 (Code)

AI 給出了一份 Logger 封裝與 API Wrapper 範例:

// AI 生成的 API 監控 Wrapper (lib/withMonitoring.ts)
import * as Sentry from "@sentry/nextjs";
import { Logtail } from "@logtail/node";

const logtail = new Logtail(process.env.LOGTAIL_SOURCE_TOKEN || "");

export function withMonitoring(handler: Function) {
  return async (req: Request) => {
    const startTime = Date.now();
    try {
      const response = await handler(req);
      logtail.info("API Request Success", {
        url: req.url,
        duration: Date.now() - startTime,
      });
      return response;
    } catch (error) {
      Sentry.captureException(error);
      logtail.error("API Request Error", {
        url: req.url,
        error: String(error),
      });
      return new Response(JSON.stringify({ error: "Internal Server Error" }), { status: 500 });
    }
  };
}


3. 修正與翻車紀錄 (Debug Experience)

這段看似簡單乾淨的 Wrapper,一套用到我們的 API 上,立刻引發了兩個嚴重的正式環境問題!

翻車點一:Edge Runtime 相容性爆掉與 Sensitive Data (PII) 洩漏

  1. Edge Runtime 崩潰:AI 引入了 @logtail/node,它使用了 Node.js 原生的 http 與 zlib 模組。然而,我們的 Next.js 串流 API (SSE) 使用了 edge 或 Serverless 輕量化執行環境,導致 Vercel 直接拋出 Module not found: Can't resolve 'net' 錯誤!
  2. 資安漏洞 (PII 敏感數據日誌化):AI 在 Log 中為了方便,打算記錄整包 Request Payload。這導致使用者的敏感財務紀錄與信用卡帳號可能被未加密直噴到 Logtail 的日誌儀表板上,嚴重違反 GDPR 與上一章(Day 23)的資安規範!

翻車點二:Sentry 上下文遺失與無窮無盡的 Serverless Client 重複初始化

在 Serverless / Next.js 環境中,每一次 API 請求都可能觸發一次檔層級的 Logger 實體建立。AI 的寫法不僅沒有進行 Singleton 模式 控制,還遺失了 Sentry 的 setUser 資訊,導致 Sentry 雖然收到報錯,但我們根本不知道是「哪一位使用者」遭遇到這起 Bug!

介入修復!

我們調整了監控架構:

  1. 使用基於 HTTP/Fetch 的輕量級 Logtail SDK(或適應 Browser/Edge 規範的 @logtail/browser / HTTP API),避免綁定 Node.js 原生模組。
  2. 實作 Sensitive Data Masking:在送出 Log 前自動過濾 password、creditCard、token 等敏感 key。
  3. 注入 Session User Context:結合 NextAuth,在進入 Handler 時自動為 Sentry 與 Logger 綁定當前使用者的 userId 與 email 雜湊值。

修正後的程式碼:

lib/logger.ts(相容 Serverless / Edge 的結構化 Logger):

import { Logtail } from "@logtail/browser"; // 使用全環境通用 SDK

const logtailToken = process.env.NEXT_PUBLIC_LOGTAIL_SOURCE_TOKEN;
const logtail = logtailToken ? new Logtail(logtailToken) : null;

export const logger = {
  info: (message: string, context?: Record<string, any>) => {
    const cleanContext = sanitizeContext(context);
    console.log(`[INFO] ${message}`, cleanContext || '');
    logtail?.info(message, cleanContext);
  },
  error: (message: string, context?: Record<string, any>) => {
    const cleanContext = sanitizeContext(context);
    console.error(`[ERROR] ${message}`, cleanContext || '');
    logtail?.error(message, cleanContext);
  }
};

// 關鍵:脫敏函式,防止 PII 敏感數據落入 Log 系統
function sanitizeContext(context?: Record<string, any>) {
  if (!context) return undefined;
  const sanitized = { ...context };
  const sensitiveKeys = ['password', 'token', 'secret', 'cardNumber', 'apiKey'];
  
  for (const key of Object.keys(sanitized)) {
    if (sensitiveKeys.some(k => key.toLowerCase().includes(k))) {
      sanitized[key] = '[REDACTED]';
    }
  }
  return sanitized;
}

lib/withMonitoring.ts(結合 Sentry 與 Logtail 的 API Wrapper):

import * as Sentry from "@sentry/nextjs";
import { logger } from "./logger";
import { getServerSession } from "next-auth";
import { authOptions } from "@/lib/auth";

export function withMonitoring(handler: Function) {
  return async (req: Request, ...args: any[]) => {
    const startTime = Date.now();
    const url = new URL(req.url).pathname;
    let userId = 'anonymous';

    try {
      // 1. 注入使用者上下文
      const session = await getServerSession(authOptions);
      if (session?.user?.id) {
        userId = session.user.id;
        Sentry.setUser({ id: userId });
      }

      // 2. 執行原始 API 邏輯
      const response = await handler(req, ...args);
      const duration = Date.now() - startTime;

      // 3. 記錄成功結構化日誌
      logger.info(`API Success: ${req.method} ${url}`, {
        method: req.method,
        url,
        status: response.status,
        duration: `${duration}ms`,
        userId,
      });

      return response;
    } catch (error) {
      const duration = Date.now() - startTime;
      
      // 4. 自動觸發 Sentry 告警並攜帶上下文
      Sentry.withScope((scope) => {
        scope.setTag("api_path", url);
        scope.setExtra("duration_ms", duration);
        Sentry.captureException(error);
      });

      // 5. 記錄 Error 日誌
      logger.error(`API Error: ${req.method} ${url}`, {
        method: req.method,
        url,
        duration: `${duration}ms`,
        userId,
        errorMessage: error instanceof Error ? error.message : String(error),
      });

      return new Response(
        JSON.stringify({ error: "Internal Server Error", requestId: Sentry.lastEventId() }), 
        { status: 500, headers: { "Content-Type": "application/json" } }
      );
    }
  };
}


學習小結

  1. 可觀察性(Observability)是驗證系統穩定度的「最後一哩路」:
    在開發環境中,我們習慣在終端機看 console.log,但到了正式環境(Serverless / Vercel),console.log 會隨即散落在無數個微短生命週期的 Function 執行個體中。只有透過 Logtail 這樣的結構化日誌收集器,結合 Sentry 的 Exception 堆疊追蹤,才能在使用者提出客訴前,主動發現並修復 Bug。
  2. AI 在整合第三方 Monitor 時的架構邊界:
    AI 很容易忘記 Serverless / Edge Runtime 的模組限制(如 Node.js Native API),也缺乏「個資脫敏(PII Redaction)」的警覺。全棧工程師必須擔任 Architect 品管 的角色,將脫敏機制(sanitizeContext)與 Auth Session 重構進高階 Wrapper(HOC / Middleware)中,維持系統的乾淨與安全。

上一篇
# Day 26:一鍵部署上雲:正式部署至 Vercel / Render / AWS
下一篇
Day 28:遭遇重寫與重構(Refactoring):當 AI 生成的程式碼變成 Technical Debt 時的解決方案
系列文
Vibe Mode 開啟:30 天用 AI 打造網頁,邊做邊學 JavaScript 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言